﻿/////////////////////////////////////////////////////////////////////////////////
//
// InstantCube
// - Automatically generates Analysis Services cubes
// (C) Copyright Johan Åhlén 2009
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//
// More info on InstantCube website: http://instantcube.codeplex.com
//
/////////////////////////////////////////////////////////////////////////////////


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace AdvantIQ.InstantCube.Model
{
    public class Dimension
    {
        public string ID { get; set; }
        public string DefaultMember { get; set; }
        public string KeyAttribute { get; set; }
        public Attributes Attributes { get; set; }
        public Hierarchies Hierarchies { get; set; }
        public Values Values { get; set; }
        public string[] InsertFieldNames { get; set; }

        private Project project;

        protected Dimension(Project project)
        {
            this.project = project;
        }

        public Dimension(XElement srcNode, Project project)
        {
            try
            {
                this.project = project;

                ID = srcNode.Attribute("id").Value;
                DefaultMember = srcNode.Attribute("defaultMember").SafeValue();
                Attributes = new Attributes(srcNode.Element("attributes"), project);
                Hierarchies = new Hierarchies(srcNode.Element("hierarchies"));
                KeyAttribute = srcNode.Attribute("keyAttribute").SafeValue();
                Values = new Values(srcNode.Element("values"));
                if (srcNode.Element("values") != null)
                    InsertFieldNames = srcNode.Element("values").Attribute("columns").Value.Split(',').Select(e => e.Trim()).ToArray();
                else
                    InsertFieldNames = new string[0];

                AddAttributeRelationships(srcNode.Element("attributeRelationships"));

                foreach (var attribute in Attributes.Values)
                {
                    attribute.Visible = Hierarchies.Count() == 0;
                }

                if (Attributes.Values.Any(e => e.IsParentRef))
                {
                    var parentChildAttribute = Attributes.Values.Single(e => e.IsParentRef);
                    Hierarchies.Add(new Hierarchy(new[] { parentChildAttribute.ID }, "Hierarchy") { IsParentChildHierarchy = true });
                }

                if (Values.Count > 0 && Hierarchies.GetParentChildHierarchy() != null)
                    UpdateParentChildReferences();
            }
            catch (ModelErrorException ex)
            {
                throw new ModelErrorException(this, ex);
            }
            catch (Exception ex)
            {
                throw new ModelErrorException(this, ex.Message);
            }
        }

        private void UpdateParentChildReferences()
        {
            var tmp = InsertFieldNames.ToList();
            var naturalKeyIndex = tmp.IndexOf(GetNaturalKeyAttribute().Key.FieldName);
            var parentIndex = tmp.IndexOf(GetParentKey());

            var Keys = new SortedList<string, int>(Values.Count);
            for (int i = 0; i < Values.Count; i++)
                Keys.Add(Values[i][naturalKeyIndex], i + 1);    // Notice that identities start by 1

            for (int i = 0; i < Values.Count; i++)
            {
                var naturalKey = Values[i][parentIndex];
                var surrogateKey = Keys.ContainsKey(naturalKey) ? Keys[naturalKey] : -1;
                Values[i][parentIndex] = surrogateKey.ToString();
            }
        }

        public override string ToString()
        {
            if (ID != null)
                return string.Format("Dimension({0})", ID);
            else
                return "Dimension";
        }

        public string GetTableName()
        {
            return project.Namings.DimPrefix + ID;
        }

        public virtual string GetPrimaryKey()
        {
            return GetTableName() + project.Namings.SurrogateKeySuffix;
        }

        public string GetParentKey()
        {
            var tmp = Hierarchies.Where(h => h.IsParentChildHierarchy).ToArray();
            if (tmp.Length != 1)
                throw new ModelErrorException(this, "GetParentKey(): could not find parent child hierarchy");

            return tmp[0].GetParentKey();
        }

        public Attribute GetNaturalKeyAttribute()
        {
            return Attributes[KeyAttribute];
        }

        public virtual List<FieldInfo> GetFields()
        {
            var lst = new List<FieldInfo>();

            lst.Add(new FieldInfo(GetPrimaryKey(),
                FieldType.SurrogateKey,
                project
            ));

            //var parentChildHierarchy = Hierarchies.GetParentChildHierarchy();
            //if (parentChildHierarchy != null)
            //{
            //    lst.Add(new FieldInfo(GetParentKey(),
            //        FieldType.Attribute, project.DefaultTypes.SurrogateKey, ""));
            //}

            foreach (var attribute in Attributes.Values)
            {
                foreach (var part in attribute.Parts)
                {
                    lst.Add(new FieldInfo(part.FieldName,
                        FieldType.Attribute, part.FieldType, "NOT NULL" ));
                }
            }

            return lst;
        }

        public virtual string GetKeyAttribute()
        {
            return GetPrimaryKey();
        }

        protected void AddAttributeRelationships(XElement node)
        {
            if (node == null)
                return;

            foreach (var r in node.Elements("attributeRelationship"))
            {
                var baseAttribute = Attributes[r.Attribute("baseAttribute").Value];
                var type = r.Attribute("type").Value;
                var dependentAttributes = r.Attribute("dependentAttribute").Value;

                if (type == "rigid")
                    baseAttribute.RelatedRigidAttributes.Add(dependentAttributes);
                else if (type == "flexible")
                    baseAttribute.RelatedFlexibleAttributes.Add(dependentAttributes);
                else
                    throw new Exception("Dimension.AddAttributeRelationships(): Invalid type");
            }
        }
    }
}
